/****************************************************
	文件：UIMgr.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2020/10/4 20:33:22
	功能：UI管理器
*****************************************************/

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using HTUtility;
using HTFramework.ResKit;

namespace HTFramework.UIKit
{
    public class UIMgr : Singleton<UIMgr>
    {
        /// <summary>
        /// UI 根节点
        /// </summary>
        public RectTransform UIRoot { get; private set; }
        /// <summary>
        /// 窗口根节点
        /// </summary>
        public RectTransform WndRoot { get; private set; }
        /// <summary>
        /// UGUI 的事件系统
        /// </summary>
        public EventSystem EventSys { get; private set; }
        /// <summary>
        /// UI摄像机
        /// </summary>
        public Camera UICamera { get; private set; }

        /// <summary>
        /// 屏幕宽高比
        /// </summary>
        private float mWidthHeightRatio;
        /// <summary>
        /// 缓存所有Window类型（key：window name；value：window type）
        /// </summary>
        private Dictionary<string, Type> mWndTypeDict;
        /// <summary>
        /// 使用字典缓存所有加载出来的Window（包含隐藏的）
        /// </summary>
        private Dictionary<string, WndBase> mWndDict;
        /// <summary>
        /// 使用列表缓存所有加载出来的Window（不包含隐藏的）
        /// </summary>
        private List<WndBase> mWndList;

        /// <summary>
        /// EventRecord 类对象池
        /// </summary>
        public ClassObjectPool<EventRecord> EventRecordPool;

        public void Init(RectTransform uiRoot, RectTransform wndTrans, EventSystem eventSystem, Camera camera)
        {
            UIRoot = uiRoot;
            WndRoot = wndTrans;
            EventSys = eventSystem;
            UICamera = camera;
            //mUICamera.orthographicSize：正交摄像机高度的一半
            mWidthHeightRatio = Screen.width / (UICamera.orthographicSize * 2.0f);

            mWndTypeDict = new Dictionary<string, Type>();
            mWndDict = new Dictionary<string, WndBase>();
            mWndList = new List<WndBase>();

            EventRecordPool = ClassPoolMgr.Instance.GetOrCreateObjectPool<EventRecord>(50);

            HTLogger.Info("UIMgr init done.");
        }
        /// <summary>
        /// 注册Window类型到缓存中
        /// </summary>
        /// <param name="panelName">panel name</param>
        /// <param name="wnd">window 对象</param>
        public void Register<T>(string panelName) where T : WndBase
        {
            if (mWndTypeDict.ContainsKey(panelName) == false)
            {
                mWndTypeDict.Add(panelName, typeof(T));
            }
        }
        /// <summary>
        /// 打开 Window
        /// </summary>
        /// <param name="panelName">panel name</param>
        /// <param name="isTop">是否置顶</param>
        /// <param name="paramLst">参数列表</param>
        public WndBase OpenWnd(string panelName, bool isTop = true, params object[] paramLst)
        {
            WndBase wnd = FindWndByPanelName<WndBase>(panelName);
            if (wnd == null) //缓存中不存在，新创建
            {
                HTLogger.Debug("缓存中不存在：{0}", panelName);
                Type wndType = GetWndType(panelName);
                if (wndType != null)
                {
                    HTLogger.Debug("反射创建 wnd：{0}", panelName);
                    wnd = System.Activator.CreateInstance(wndType) as WndBase; //反射创建 window 对象
                }
                HTLogger.Debug("同步加载、实例化 wnd：{0}", panelName);
                GameObject wndGO = GOLoader.Instance.LoadSync(string.Format("{0}{1}.prefab", HTDemo01.GConsts.UI_WINDOW_PREFABS_PATH, panelName)); //此处字符串拼接会有GC
                wnd.GameObject = wndGO;
                wnd.Transform = wndGO.transform;
                wnd.PanelName = panelName;
                //设置到 Window Root 下
                wnd.Transform.SetParent(WndRoot, false);
                wnd.OnAwake(paramLst);
                wnd.OnShow(paramLst);
                HTLogger.Debug("将 {0} wnd 添加到缓存中", panelName);
                mWndDict.Add(panelName, wnd); //缓存
                mWndList.Add(wnd);
            }
            else //缓存中已经存在
            {
                HTLogger.Debug("缓存中已存在：{0}", panelName);
                if (mWndList.Contains(wnd) == false)
                {
                    mWndList.Add(wnd);
                }
                ShowWnd(wnd, isTop, paramLst);
            }
            return wnd;
        }
        /// <summary>
        /// 根据 panel name 关闭 window
        /// </summary>
        /// <param name="panelName">panel name</param>
        /// <param name="destroy">是否删除资源</param>
        public void CloseWnd(string panelName, bool destroy = false)
        {
            WndBase wnd = FindWndByPanelName<WndBase>(panelName);
            CloseWnd(wnd, destroy);
        }
        /// <summary>
        /// 根据 window 对象关闭 window
        /// </summary>
        /// <param name="wnd">window 对象</param>
        /// <param name="destroy">是否删除资源</param>
        public void CloseWnd(WndBase wnd, bool destroy = false)
        {
            if (wnd == null)
            {
                HTLogger.Error("CloseWnd error，wnd null！");
                return;
            }
            if (System.Object.ReferenceEquals(wnd.GameObject, null))
            {
                HTLogger.Error("CloseWnd error，wnd GameObject is null！");
                return;
            }
            wnd.OnHide();
            wnd.OnDestory();
            if (mWndList.Contains(wnd)) //从记录当前显示的UI缓存中移除
            {
                HTLogger.Debug("从 mWndList 缓存中移除 ui name：{0}", wnd.PanelName);
                mWndList.Remove(wnd);
            }
            if (destroy) //释放内存
            {
                HTLogger.Debug("释放内存，ui name：{0}", wnd.PanelName);
                GOLoader.Instance.Release(wnd.GameObject, true, false);
                mWndDict.Remove(wnd.PanelName);
                GameObject.DestroyImmediate(wnd.GameObject);
                wnd = null;
            }
            else //不释放内存，只隐藏
            {
                HTLogger.Debug("不释放内存，ui name：{0}", wnd.PanelName);
                GOLoader.Instance.Release(wnd.GameObject, false, false);
            }
        }
        /// <summary>
        /// 关闭所有 Window （默认不删除资源）
        /// </summary>
        /// <param name="destroy">是否删除资源</param>
        public void CloseAllWnd(bool destroy = false)
        {
            for (int i = mWndList.Count - 1; i >= 0; i--)
            {
                CloseWnd(mWndList[i], destroy);
            }
        }
        /// <summary>
        /// 根据 panel name 显示 wnd
        /// </summary>
        /// <param name="panelName">panel name</param>
        /// <param name="isTop">是否置顶</param>
        /// <param name="paramLst">参数列表</param>
        public void ShowWnd(string panelName, bool isTop, params object[] paramLst)
        {
            WndBase wnd = FindWndByPanelName<WndBase>(panelName);
            ShowWnd(wnd, isTop, paramLst);
        }
        /// <summary>
        /// 根据 window 对象 显示 wnd
        /// </summary>
        /// <param name="wnd">window 对象</param>
        /// <param name="isTop">是否置顶</param>
        /// <param name="paramLst">参数列表</param>
        public void ShowWnd(WndBase wnd, bool isTop, params object[] paramLst)
        {
            if (wnd != null)
            {
                if (System.Object.ReferenceEquals(wnd.GameObject, null) == false && wnd.GameObject.activeSelf == false)
                {
                    HTLogger.Debug("UIManager show {0} wnd", wnd.PanelName);
                    wnd.GameObject.SetActive(true);
                    if (isTop)
                    {
                        wnd.Transform.SetAsLastSibling();
                    }
                    wnd.OnShow(paramLst);
                }
            }
        }
        /// <summary>
        /// 根据 panel name 隐藏 window
        /// </summary>
        /// <param name="panelName">panel name</param>
        public void HideWnd(string panelName)
        {
            WndBase wnd = FindWndByPanelName<WndBase>(panelName);
            HideWnd(wnd);
        }
        /// <summary>
        /// 根据 window 对象隐藏 window
        /// </summary>
        /// <param name="wnd">window 对象</param>
        public void HideWnd(WndBase wnd)
        {
            if (wnd == null)
            {
                HTLogger.Debug("HideWnd error，wnd is null！");
                return;
            }
            HTLogger.Debug("UIManager hide {0} wnd", wnd.PanelName);
            wnd.GameObject.SetActive(false);
            wnd.OnHide();
        }
        /// <summary>
        /// 跳转到指定 Window
        /// </summary>
        /// <param name="targetPanelName">目标 panel name</param>
        /// <param name="isTop">是否置顶</param>
        /// <param name="paramLst">参数列表</param>
        public void SwitchToWnd(string targetPanelName, bool isTop = true, params object[] paramLst)
        {
            CloseAllWnd();
            OpenWnd(targetPanelName, isTop, paramLst);
        }
        /// <summary>
        /// 发送窗口消息
        /// </summary>
        /// <param name="panelName">panel name</param>
        /// <param name="msgType">UIManager 中定义的消息类型</param>
        /// <param name="paramLst">参数列表</param>
        /// <returns></returns>
        public bool SendWndMsg(string panelName, WndMsgType msgType, params object[] paramLst)
        {
            WndBase wnd = FindWndByPanelName<WndBase>(panelName);
            if (wnd == null)
            {
                HTLogger.Error("SendWndMsg error，缓存中不存在 {0} window！", panelName);
                return false;
            }
            wnd.OnWndMsg(msgType, paramLst);
            return true;
        }
        /// <summary>
        /// 设置 UI 状态（整体是显示还是隐藏）
        /// </summary>
        /// <param name="activate">状态</param>
        public void SetUIStatus(bool activate)
        {
            if (UIRoot != null)
            {
                UIRoot.gameObject.SetActive(activate);
            }
        }
        /// <summary>
        /// 设置默认选中的物体
        /// </summary>
        /// <param name="obj">默认选中的物体</param>
        public void SetDefaultSelector(GameObject obj)
        {
            if (EventSys == null)
            {
                EventSys = EventSystem.current;
            }
            EventSys.firstSelectedGameObject = obj;
        }

        /// <summary>
        /// 帧循环
        /// </summary>
        public void OnUpdate()
        {
            for (int i = 0; i < mWndList.Count; i++)
            {
                mWndList[i].OnUpdate();
            }
        }
        /// <summary>
        /// 从 mWndDict 缓存中获取 Window 对象
        /// </summary>
        /// <param name="panelName">panel name</param>
        /// <returns>window 对象</returns>
        private T FindWndByPanelName<T>(string panelName) where T : WndBase
        {
            WndBase wnd = null;
            if (mWndDict.TryGetValue(panelName, out wnd) && wnd != null)
            {
                return wnd as T;
            }
            return null;
        }
        /// <summary>
        /// 从 mWndTypeDict 缓存中获取 Window type 类型
        /// </summary>
        /// <param name="panelName">panel name</param>
        /// <returns>window type</returns>
        private Type GetWndType(string panelName)
        {
            Type t = null;
            if (mWndTypeDict.TryGetValue(panelName, out t) == false)
            {
                HTLogger.Error("获取 {0} window 类型失败，尚未注册该类型！", panelName);
            }
            return t;
        }
        /// <summary>
        /// 初始化 UI Components （递归搜索所有符合命名规范的UI子物体，存入Dict中）
        /// </summary>
        /// <param name="comps">存储UI组件的字典</param>
        /// <param name="obj">当前GameObject</param>
        public void InitUIComponents(ref Dictionary<string, UnityEngine.Object> comps, GameObject obj)
        {
            if (obj == null) return;
            if (obj.transform.childCount > 0)
            {
                string name = string.Empty;
                string headStr = string.Empty;
                GameObject curGo = null;
                for (int i = 0; i < obj.transform.childCount; i++)
                {
                    curGo = obj.transform.GetChild(i).gameObject;
                    name = curGo.name;
                    if (name.Contains("_"))
                    {
                        headStr = name.Split('_')[0];
                        switch (headStr)
                        {
                            case "Obj":
                                comps.Add(name, curGo);
                                break;
                            case "Trans":
                                comps.Add(name, curGo.transform);
                                break;
                            case "Txt":
                                comps.Add(name, curGo.GetComponent<Text>());
                                break;
                            case "Img":
                                comps.Add(name, curGo.GetComponent<Image>());
                                break;
                            case "RImg":
                                comps.Add(name, curGo.GetComponent<RawImage>());
                                break;
                            case "Btn":
                                comps.Add(name, curGo.GetComponent<Button>());
                                break;
                            case "Tog":
                                comps.Add(name, curGo.GetComponent<Toggle>());
                                break;
                            case "Sli":
                                comps.Add(name, curGo.GetComponent<Slider>());
                                break;
                            case "Scrbar":
                                comps.Add(name, curGo.GetComponent<Scrollbar>());
                                break;
                            case "Dropdown":
                                comps.Add(name, curGo.GetComponent<Dropdown>());
                                break;
                            case "InputField":
                                comps.Add(name, curGo.GetComponent<InputField>());
                                break;
                            case "ScrView":
                                comps.Add(name, curGo.GetComponent<ScrollRect>());
                                break;
                            case "Cam":
                                comps.Add(name, curGo.GetComponent<Camera>());
                                break;
                            case "Col":
                                comps.Add(name, curGo.GetComponent<BoxCollider>());
                                break;
                            default:
                                HTLogger.Error("尚未定义的UIComps前缀：{0}", headStr);
                                break;
                        }
                    }
                    InitUIComponents(ref comps, curGo);
                }
            }
        }
    }
}
